package com.wowocai.btchina.utils.ip;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.com.bytecode.opencsv.CSVReader;

/**
 * 类描述： 解析IP工具类 创建人： ZhangYi 创建时间：2015-7-22 上午11:03:55
 */
// @Service("iPPoolUtil")
public class IPPoolUtil {

    private static long aBegin = 167772160l;

    private static long aEnd = 184549375l;

    private static long bBegin = 2886729728l;

    private static long bEnd = 2887778303l;

    private static long cBegin = 3232235520l;

    private static long cEnd = 3232301055l;

    /**
     * IP缓存
     */
    private static TreeMap<Long, RangeRow> ipPollCache = null;

    private static final String blocksPath = "ip/GeoIP.csv";

    private static final String locationsPath = "ip/GeoIP2-Country-Locations-zh-CN.csv";

    private static final Logger logger = LoggerFactory.getLogger(IPPoolUtil.class);

    private static IPPoolUtil instance = null;

    private static final Area INNER_AREA = new Area("NONE", "INNER", "内网IP", null);

    /**
     * 默认时区为东8区
     */
    public static final String TIME_ZONE = "GMT+08";

    /**
     * Creates a new instance of GeoFreeIpUtil.
     */
    private IPPoolUtil() {
        initIpRange();
    }

    public static synchronized IPPoolUtil getInstance() {
        if (instance == null) {
            instance = new IPPoolUtil();
        }
        return instance;
    }

    /**
     * 方法描述：初始化IP段信息
     */
    private synchronized void initIpRange() {

        ipPollCache = new TreeMap<Long, RangeRow>();
        // 初始化国家信息
        Map<String, Area> countryMap = initGeonameId();
        // 读取IP文件的物理路径
        // String rangeAbsPath = IPPoolUtil.class.getClassLoader().getResource(blocksPath).getPath();
        try {
            CSVReader reader = new CSVReader(
                    new InputStreamReader(IPPoolUtil.class.getClassLoader().getResourceAsStream(blocksPath), "UTF-8"));
            String[] nextLine;

            while ((nextLine = reader.readNext()) != null) {

                if (nextLine.length == 7 && nextLine[3].matches("[0-9]+")) {

                    Long begin = ObjectUtil.getLong(nextLine[3]);

                    Long end = ObjectUtil.getLong(nextLine[4]);

                    String ccode = ObjectUtil.getString(nextLine[5]);

                    RangeRow rangeRow = new RangeRow();
                    rangeRow.setBegin(begin);
                    rangeRow.setEnd(end);
                    rangeRow.setCcode(ccode);
                    rangeRow.setArea(countryMap.get(ccode));

                    ipPollCache.put(end, rangeRow);
                }
            }
            reader.close();
            logger.info("......IP段信息加载完成(" + ipPollCache.size() + ")........");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 方法描述：初始化国家信息
     */
    private Map<String, Area> initGeonameId() {

        Map<String, Area> countryMap = new HashMap<String, Area>();

        // 读取国家文件的物理路径
        // String rangeAbsPath = IPPoolUtil.class.getClassLoader().getResource(locationsPath).getPath();
        try {
            CSVReader reader = new CSVReader(new InputStreamReader(
                    IPPoolUtil.class.getClassLoader().getResourceAsStream(locationsPath), "UTF-8"));
            String[] nextLine;
            while ((nextLine = reader.readNext()) != null) {
                // 国家ID
                String geonameId = nextLine[0];
                String countryCn = nextLine[6];
                // 国家代码
                String ccode = nextLine[4];
                // 国家代码_备用，如果没得就取大洲编码
                String ccode_bak = nextLine[2];

                // 默认版本没得时区，需要手动更新
                // 国家时区
                String timeZone = nextLine[5];

                // 选取最终国家代码
                if (StringUtils.isBlank(ccode)) {
                    ccode = ccode_bak;
                }
                if (StringUtils.isBlank(timeZone)) {
                    timeZone = TIME_ZONE;
                }

                if (Pattern.compile("[0-9]*").matcher(geonameId).matches()) {
                    Area area = new Area();
                    area.setGeonameId(ObjectUtil.getInt(geonameId));
                    area.setCode(ccode);
                    area.setTimeZone(timeZone);
                    area.setCountryCn(countryCn);
                    countryMap.put(ccode, area);
                }
                // end
            }
            reader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return countryMap;
    }

    /**
     * 根据起始IP地址和子网掩码计算终止IP
     */
    public Nets getEndIP(String StartIP, int netmask) {
        return getEndIP(StartIP, getMask(netmask));
    }

    /**
     * 根据起始IP地址和子网掩码计算终止IP
     */
    public Nets getEndIP(String StartIP, String netmask) {
        Nets nets = new Nets();
        String[] start = Negation(StartIP, netmask).split("\\.");
        nets.setStartIP(start[0] + "." + start[1] + "." + start[2] + "." + (Integer.valueOf(start[3]) + 1));
        nets.setEndIP(TaskOR(Negation(StartIP, netmask), netmask));
        nets.setNetMask(netmask);
        return nets;
    }

    /**
     * 根据掩码位计算掩码
     */
    public String getMask(int masks) {
        if (masks == 1)
            return "128.0.0.0";
        else if (masks == 2)
            return "192.0.0.0";
        else if (masks == 3)
            return "224.0.0.0";
        else if (masks == 4)
            return "240.0.0.0";
        else if (masks == 5)
            return "248.0.0.0";
        else if (masks == 6)
            return "252.0.0.0";
        else if (masks == 7)
            return "254.0.0.0";
        else if (masks == 8)
            return "255.0.0.0";
        else if (masks == 9)
            return "255.128.0.0";
        else if (masks == 10)
            return "255.192.0.0";
        else if (masks == 11)
            return "255.224.0.0";
        else if (masks == 12)
            return "255.240.0.0";
        else if (masks == 13)
            return "255.248.0.0";
        else if (masks == 14)
            return "255.252.0.0";
        else if (masks == 15)
            return "255.254.0.0";
        else if (masks == 16)
            return "255.255.0.0";
        else if (masks == 17)
            return "255.255.128.0";
        else if (masks == 18)
            return "255.255.192.0";
        else if (masks == 19)
            return "255.255.224.0";
        else if (masks == 20)
            return "255.255.240.0";
        else if (masks == 21)
            return "255.255.248.0";
        else if (masks == 22)
            return "255.255.252.0";
        else if (masks == 23)
            return "255.255.254.0";
        else if (masks == 24)
            return "255.255.255.0";
        else if (masks == 25)
            return "255.255.255.128";
        else if (masks == 26)
            return "255.255.255.192";
        else if (masks == 27)
            return "255.255.255.224";
        else if (masks == 28)
            return "255.255.255.240";
        else if (masks == 29)
            return "255.255.255.248";
        else if (masks == 30)
            return "255.255.255.252";
        else if (masks == 31)
            return "255.255.255.254";
        else if (masks == 32)
            return "255.255.255.255";
        return "";
    }

    /**
     * temp1根据temp2取反
     */
    private String Negation(String StartIP, String netmask) {
        String[] temp1 = StartIP.trim().split("\\.");
        String[] temp2 = netmask.trim().split("\\.");
        int[] rets = new int[4];
        for (int i = 0; i < 4; i++) {
            rets[i] = Integer.parseInt(temp1[i]) & Integer.parseInt(temp2[i]);
        }
        return rets[0] + "." + rets[1] + "." + rets[2] + "." + rets[3];
    }

    /**
     * temp1根据temp2取或
     */
    private String TaskOR(String StartIP, String netmask) {
        String[] temp1 = StartIP.trim().split("\\.");
        String[] temp2 = netmask.trim().split("\\.");
        int[] rets = new int[4];
        for (int i = 0; i < 4; i++) {
            rets[i] = 255 - (Integer.parseInt(temp1[i]) ^ Integer.parseInt(temp2[i]));
        }
        // 根据需求注释修改
        // return rets[0]+"."+rets[1]+"."+rets[2]+"."+(rets[3]-1);
        return rets[0] + "." + rets[1] + "." + rets[2] + "." + (rets[3]);
    }

    /**
     * 计算子网大小
     */
    public int getPoolMax(int netmask) {
        if (netmask <= 0 || netmask >= 32) {
            return 0;
        }
        int bits = 32 - netmask;
        return (int) Math.pow(2, bits) - 2;
    }

    /**
     * 转换为验码位数
     */
    public int getNetMask(String netmarks) {
        StringBuffer sbf;
        String str;
        int inetmask = 0, count = 0;
        String[] ipList = netmarks.split("\\.");
        for (int n = 0; n < ipList.length; n++) {
            sbf = toBin(Integer.parseInt(ipList[n]));
            str = sbf.reverse().toString();
            count = 0;
            for (int i = 0; i < str.length(); i++) {
                i = str.indexOf('1', i);
                if (i == -1) {
                    break;
                }
                count++;
            }
            inetmask += count;
        }
        return inetmask;
    }

    private StringBuffer toBin(int x) {
        StringBuffer result = new StringBuffer();
        result.append(x % 2);
        x /= 2;
        while (x > 0) {
            result.append(x % 2);
            x /= 2;
        }
        return result;
    }

    /**
     * 方法描述：将IP转化为整数
     */
    public long convertIpToLong(String ip) {
        String[] checkIp = ip.split("\\.", 4);
        long intIp = 0;

        for (int i = 3, j = 0; i >= 0 && j <= 3; i--, j++) {
            intIp += Integer.parseInt(checkIp[j]) * Math.pow(256, i);
        }
        return intIp;
    }

    /**
     * 方法描述：根据IP解析国家
     */
    public Area getCcodeByIP(String ip) {
        if (ip == null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {
            return null;
        }
        if (ipPollCache == null) {
            initIpRange();
        }
        if (isInnerIP(ip)) {
            return INNER_AREA;
        }
        // 将IP转化为整数
        Long ipConvert = convertIpToLong(ip);
        try {
            RangeRow range = ipPollCache.ceilingEntry(ipConvert).getValue();
            if (range.getBegin() <= ipConvert && range.getEnd() >= ipConvert) {
                return range.getArea();
            } else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 方法描述：初始化内网IP
     */
    public void initIntranetIp() {
        // aBegin = getIpNum("10.0.0.0");
        // aEnd = getIpNum("10.255.255.255");
        // bBegin = getIpNum("172.16.0.0");
        // bEnd = getIpNum("172.31.255.255");
        // cBegin = getIpNum("192.168.0.0");
        // cEnd = getIpNum("192.168.255.255");
    }

    /**
     * 方法描述： IP转换为long类型
     * 
     * @param ipAddress
     * @return
     */
    private long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);

        long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
        return ipNum;
    }

    /**
     * 方法描述：判断IP地址是否在某个范围
     * 
     * @param userIp
     * @param begin
     * @param end
     * @return
     */
    private boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    /**
     * 方法描述： 判断是否为内网IP
     * 
     * @param ipAddress
     * @return
     */
    public boolean isInnerIP(String ipAddress) {
        boolean isInnerIp = false;
        long ipNum = getIpNum(ipAddress);
        /**
         * 私有IP：A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类
         * 192.168.0.0-192.168.255.255 当然，还有127这个网段是环回地址
         **/
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
                || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    // public static void main(String[] args) {
    // // String ip = "134.35.97.122";
    // String ip = "61.128.128.68";
    // IPPoolUtil instance = getInstance();
    // System.out.println(instance.getCcodeByIP(ip));
    //
    // }

}
